#include "StdAfx.h"
/// Copyright 2012, Alex Averian, Yonan Research
/// Released under BSD Licence


//imaging manipulation, viewer
//#include <Wincodec.h>
#pragma comment(lib, "Windowscodecs.lib")


#include "..\qcom.h"
using namespace micro::com;


#include "qwic.h"
using namespace micro::directx;


//
// Get a WIC Imaging factory
//
HRESULT QWic::GetGlobalFactory( IWICImagingFactory ** factory )
{
	if ( nullptr == factory )
		return E_POINTER;

	static QComPointer<IWICImagingFactory> m_pWICFactory;

	if ( ! m_pWICFactory )
	{
		//CLSID_WICImagingFactory1, CLSID_WICImagingFactory2 on Windows8

		RINOK( CoCreateInstance( CLSID_WICImagingFactory,
			nullptr,
			CLSCTX_INPROC_SERVER,
			IID_PPV_ARGS( &m_pWICFactory ) ) );

	}


	RINOK( m_pWICFactory.QueryInterface( IID_PPV_ARGS( factory ) ) );
	return S_OK;
}

HRESULT QWic::GetLocalFactory( IWICImagingFactory ** factory )
{
	if ( nullptr == factory )
		return E_POINTER;


	//CLSID_WICImagingFactory1, CLSID_WICImagingFactory2 on Windows8

	RINOK( CoCreateInstance( CLSID_WICImagingFactory,
		nullptr,
		CLSCTX_INPROC_SERVER,
		IID_PPV_ARGS( factory ) ) );


	return S_OK;
}

//
// Creates a QDirect2D bitmap from the specified
// file name.
//
HRESULT QWic::CreateBitmapSourceFromFile( LPCTSTR path,
										 unsigned int destinationWidth, unsigned int destinationHeight,
										 IWICBitmapSource ** wicBitmapSource )
{
	QComPointer<IWICImagingFactory> wicFactory;
	QComPointer<IWICBitmapDecoder> decoder;
	QComPointer<IWICBitmapFrameDecode> bitmapSource;
	QComPointer<IWICStream> stream;
	QComPointer<IWICFormatConverter> converter;
	QComPointer<IWICBitmapScaler> scaler;

	RINOK( GetLocalFactory( &wicFactory ) );


	RINOK(  wicFactory->CreateDecoderFromFilename(
		path, nullptr,
		GENERIC_READ,  WICDecodeMetadataCacheOnLoad,
		&decoder ) );


	RINOK( decoder->GetFrame( 0, &bitmapSource ) );

	// Convert the image format to 32bppPBGRA
	// (DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED).
	RINOK( wicFactory->CreateFormatConverter( &converter ) );


	unsigned int originalWidth, originalHeight;
	RINOK( bitmapSource->GetSize( &originalWidth, &originalHeight ) );


	// If a new width or height was specified, create an
	// IWICBitmapScaler and use it to resize the image.
	if ( ( destinationWidth > 0 && originalWidth > destinationWidth ) || ( destinationHeight > 0 && originalHeight > destinationHeight ) )
	{

		if ( destinationWidth == 0 )
		{
			float scalar = static_cast<float>( destinationHeight ) / static_cast<float>( originalHeight );
			destinationWidth = static_cast<unsigned int>( scalar * static_cast<float>( originalWidth ) );
		}
		else
			if ( destinationHeight == 0 )
			{
				float scalar = static_cast<float>( destinationWidth ) / static_cast<float>( originalWidth );
				destinationHeight = static_cast<unsigned int>( scalar * static_cast<float>( originalHeight ) );
			}


			RINOK( wicFactory->CreateBitmapScaler( &scaler ) );
			RINOK( scaler->Initialize(
				bitmapSource,
				destinationWidth,
				destinationHeight,
				WICBitmapInterpolationModeCubic
				) );
			RINOK( converter->Initialize(
				scaler,
				GUID_WICPixelFormat32bppPBGRA,
				WICBitmapDitherTypeNone,
				nullptr,
				0.f,
				WICBitmapPaletteTypeMedianCut
				) );
	}
	else // Don't scale the image.
	{
		RINOK( converter->Initialize(
			bitmapSource,
			GUID_WICPixelFormat32bppPBGRA,
			WICBitmapDitherTypeNone,
			nullptr,
			0.f,
			WICBitmapPaletteTypeMedianCut
			) );
	}


	RINOK( converter->QueryInterface( wicBitmapSource ) );

	return S_OK;
}



//
// Creates a QDirect2D bitmap from the specified
// resource name and type.
//
HRESULT QWic::CreateBitmapSourceFromResource(
	LPCTSTR resourceName,
	LPCTSTR resourceType,
	unsigned int destinationWidth,
	unsigned int destinationHeight,
	IWICBitmapSource ** wicBitmapSource
	)
{
	HRESULT hr = S_OK;

	QComPointer<IWICImagingFactory> wicFactory;
	QComPointer<IWICBitmapDecoder> decoder;
	QComPointer<IWICBitmapFrameDecode> bitmapSource;
	QComPointer<IWICStream> stream;
	QComPointer<IWICFormatConverter> formatConverter;
	QComPointer<IWICBitmapScaler> scaler;

	HRSRC imageResHandle = nullptr;
	HGLOBAL imageResDataHandle = nullptr;
	void * imageFile = nullptr;
	unsigned long imageFileSize = 0;

	HMODULE hm  =::GetModuleHandle( 0 );

	// Locate the resource.
	imageResHandle = ::FindResource( hm , resourceName, resourceType );

	if ( imageResHandle )
	{
		// Load the resource.
		imageResDataHandle = ::LoadResource( hm, imageResHandle );

		hr = imageResDataHandle ? S_OK : E_FAIL;
	}

	if ( SUCCEEDED( hr ) )
	{
		// Lock it to get a system memory pointer.
		imageFile = ::LockResource( imageResDataHandle );

		hr = imageFile ? S_OK : E_FAIL;
	}

	if ( SUCCEEDED( hr ) )
	{
		// Calculate the size.
		imageFileSize = SizeofResource( hm, imageResHandle );

		hr = imageFileSize ? S_OK : E_FAIL;
	}

	if ( SUCCEEDED( hr ) )
	{
		hr = GetLocalFactory( &wicFactory );
	}

	if ( SUCCEEDED( hr ) )
	{
		// Create a WIC stream to map onto the memory.
		hr = wicFactory->CreateStream( &stream );
	}

	if ( SUCCEEDED( hr ) )
	{
		// Initialize the stream with the memory pointer and size.
		hr = stream->InitializeFromMemory(
			reinterpret_cast<unsigned char *>( imageFile ),
			imageFileSize
			);
	}

	if ( SUCCEEDED( hr ) )
	{
		// Create a decoder for the stream.
		hr = wicFactory->CreateDecoderFromStream(
			stream,
			nullptr,
			WICDecodeMetadataCacheOnLoad,
			&decoder
			);
	}

	if ( SUCCEEDED( hr ) )
	{
		// Create the initial frame.
		hr = decoder->GetFrame( 0, &bitmapSource );
	}

	if ( SUCCEEDED( hr ) )
	{
		// Convert the image format to 32bppPBGRA
		// (DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED).
		hr = wicFactory->CreateFormatConverter( &formatConverter );
	}

	if ( SUCCEEDED( hr ) )
	{
		// If a new width or height was specified, create an
		// IWICBitmapScaler and use it to resize the image.
		if ( destinationWidth != 0 || destinationHeight != 0 )
		{
			unsigned int originalWidth, originalHeight;
			hr = bitmapSource->GetSize( &originalWidth, &originalHeight );

			if ( SUCCEEDED( hr ) )
			{
				if ( destinationWidth == 0 )
				{
					float scalar = static_cast<float>( destinationHeight ) / static_cast<float>( originalHeight );
					destinationWidth = static_cast<unsigned int>( scalar * static_cast<float>( originalWidth ) );
				}
				else
					if ( destinationHeight == 0 )
					{
						float scalar = static_cast<float>( destinationWidth ) / static_cast<float>( originalWidth );
						destinationHeight = static_cast<unsigned int>( scalar * static_cast<float>( originalHeight ) );
					}

					hr = wicFactory->CreateBitmapScaler( &scaler );

					if ( SUCCEEDED( hr ) )
					{
						hr = scaler->Initialize(
							bitmapSource,
							destinationWidth,
							destinationHeight,
							WICBitmapInterpolationModeCubic
							);

						if ( SUCCEEDED( hr ) )
						{
							hr = formatConverter->Initialize(
								scaler,
								GUID_WICPixelFormat32bppPBGRA,
								WICBitmapDitherTypeNone,
								nullptr,
								0.f,
								WICBitmapPaletteTypeMedianCut
								);
						}
					}
			}
		}
		else
		{
			hr = formatConverter->Initialize(
				bitmapSource,
				GUID_WICPixelFormat32bppPBGRA,
				WICBitmapDitherTypeNone,
				nullptr,
				0.f,
				WICBitmapPaletteTypeMedianCut
				);
		}
	}

	RINOK( formatConverter->QueryInterface( wicBitmapSource ) );

	return S_OK;
}


// HRESULT CreateBitmapFromMemory(
// 	[in]   UINT uiWidth,
// 	[in]   UINT uiHeight,
// 	[in]   REFWICPixelFormatGUID pixelFormat,
// 	[in]   UINT cbStride,
// 	[in]   UINT cbBufferSize,
// 	[in]   BYTE *pbBuffer,
// 	[out]  IWICBitmap **ppIBitmap
// 	);


HRESULT QWic::CreateBitmapFromBitmapSource( IWICBitmapSource * source, HBITMAP & bitmap )
{

	ASSERT( source );
	bitmap = nullptr;
	HBITMAP hBitmap = nullptr;


	HRESULT hr = S_OK;

	// Get image attributes and check for valid image
	UINT width = 0;
	UINT height = 0;

	void * pvImageBits = nullptr;

	if ( SUCCEEDED( hr ) )
	{
		hr = source->GetSize( &width, &height );
	}

	// Create a DIB section based on Bitmap Info
	// BITMAPINFO Struct must first be setup before a DIB can be created.
	// Note that the height is negative for top-down bitmaps
	if ( SUCCEEDED( hr ) )
	{
		BITMAPINFO bminfo;
		ZeroMemory( &bminfo, sizeof( bminfo ) );
		bminfo.bmiHeader.biSize         = sizeof( BITMAPINFOHEADER );
		bminfo.bmiHeader.biWidth        = width;
		bminfo.bmiHeader.biHeight       = -( LONG )height;
		bminfo.bmiHeader.biPlanes       = 1;
		bminfo.bmiHeader.biBitCount     = 32;
		bminfo.bmiHeader.biCompression  = BI_RGB;

		// Get a DC for the full screen
		HDC hdcScreen = GetDC( nullptr );

		hr = hdcScreen ? S_OK : E_FAIL;


		// Release the previously allocated bitmap
		if ( SUCCEEDED( hr ) )
		{
			hBitmap = CreateDIBSection( hdcScreen, &bminfo, DIB_RGB_COLORS, &pvImageBits, nullptr, 0 );
			ReleaseDC( nullptr, hdcScreen );

			hr = hBitmap ? S_OK : E_FAIL;
		}
	}

	UINT cbStride = 0;

	if ( SUCCEEDED( hr ) )
	{
		// Size of a scan line represented in bytes: 4 bytes each pixel
		hr = UIntMult( width, sizeof( DWORD ), &cbStride );
	}

	UINT cbImage = 0;

	if ( SUCCEEDED( hr ) )
	{
		// Size of the image, represented in bytes
		hr = UIntMult( cbStride, height, &cbImage );
	}

	// Extract the image into the HBITMAP
	if ( SUCCEEDED( hr ) )
	{
		hr = source->CopyPixels(
			nullptr,
			cbStride,
			cbImage,
			reinterpret_cast<BYTE *> ( pvImageBits ) );
	}

	// Image Extraction failed, clear allocated memory
	if ( SUCCEEDED( hr ) )
	{
		bitmap = hBitmap;
	}
	else
	{

		DeleteObject( hBitmap );
	}

	return hr;
}


HRESULT QWic::CreateBitmapFromFile( LPCTSTR path, HBITMAP & bitmap, int nWidth, int nHeight )
{
	QComPointer<IWICBitmapSource> source;
	HRESULT hr = CreateBitmapSourceFromFile( path,	nWidth, nHeight, &source );

	return  CreateBitmapFromBitmapSource( source, bitmap );
}


HRESULT QWic::CreateBitmapFromResource( LPCTSTR resourceName,  LPCTSTR resourceType, HBITMAP & bitmap, int nWidth, int nHeight )
{
	QComPointer<IWICBitmapSource> source;
	HRESULT hr = CreateBitmapSourceFromResource(   resourceName,    resourceType,	nWidth, nHeight, &source );

	return  CreateBitmapFromBitmapSource( source, bitmap );
}


bool QWic::SaveImage( QBitmap & bmp,  QString & filePath, EImageType type /*= TypeJpeg */ )
{

	GUID format = GUID_ContainerFormatJpeg;

	if ( type == TypePng )
		format = GUID_ContainerFormatPng;

	if ( type == TypeTiff )
		format = GUID_ContainerFormatTiff;

	if ( type == TypeBitmap )
		format = GUID_ContainerFormatBmp;

	if ( type == TypeGif )
		format = GUID_ContainerFormatGif;



	QComPointer<IWICImagingFactory> piFactory;
	RINOK2( QWic::GetLocalFactory( &piFactory ) );

	QComPointer<IWICBitmap>	pSource;
	RINOK2( piFactory->CreateBitmapFromHBITMAP( bmp, nullptr, WICBitmapUseAlpha, &pSource ) );



	QComPointer<IWICFormatConverter> converter;
	RINOK2( piFactory->CreateFormatConverter( &converter ) );


	RINOK2( converter->Initialize( pSource, GUID_WICPixelFormat24bppBGR,
		WICBitmapDitherTypeNone,
		nullptr,  0.f,  WICBitmapPaletteTypeMedianCut  ) );


	QComPointer<IWICStream> piStream;
	RINOK2( piFactory->CreateStream( &piStream ) );
	RINOK2( piStream->InitializeFromFilename( filePath, GENERIC_WRITE ) );



	QComPointer<IWICBitmapEncoder>  piEncoder;
	RINOK2( piFactory->CreateEncoder( format, NULL, &piEncoder ) );
	RINOK2( piEncoder->Initialize( piStream, WICBitmapEncoderNoCache ) );



	//optional
	QComPointer<IPropertyBag2> pPropertybag;
	QComPointer<IWICBitmapFrameEncode> piBitmapFrame;
	RINOK2( piEncoder->CreateNewFrame( &piBitmapFrame, &pPropertybag ) );


	//jpeg si tiff
	//CompressionQuality	VT_R4	0 - 1.0

	//doar pentru  tiff-uri
	//This is how you customize the TIFF output.
	// 	PROPBAG2 option = { 0 };
	// 	option.pstrName = L"TiffCompressionMethod";
	//
	// 	VARIANT varValue;
	// 	VariantInit( &varValue );
	// 	varValue.vt = VT_UI1;
	// 	varValue.bVal = WICTiffCompressionLZW;
	//
	//  RINOK2( pPropertybag->Write( 1, &option, &varValue ) );

	RINOK2( piBitmapFrame->Initialize( pPropertybag ) ); //ok

	//OPTIONAL
	//piBitmapFrame->SetSize( uiWidth, uiHeight );
	//WICPixelFormatGUID formatGUID = GUID_WICPixelFormat24bppBGR;
	//hr = piBitmapFrame->SetPixelFormat( &formatGUID );


	RINOK2( piBitmapFrame->WriteSource( pSource, nullptr ) );
	RINOK2( piBitmapFrame->Commit() );
	RINOK2( piEncoder->Commit() );


	return true;
}

//void LoadPictureFile(LPCTSTR szFile,LPPICTURE &gpPicture)
//{
//HANDLE hFile = CreateFile(szFile, GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr); // open file
//ASSERT(INVALID_HANDLE_VALUE != hFile);
//
//DWORD dwFileSize = GetFileSize(hFile, nullptr); // get file size
//ASSERT(-1 != dwFileSize);
//
//HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize); // alloc memory based on file size
//ASSERT(nullptr != hGlobal);
//
//LPVOID d=GlobalLock(hGlobal);
//ASSERT(d!=nullptr);
//
//unsigned long s=0;
//ReadFile(hFile, d, dwFileSize, &s, nullptr); // read file and store in global memory
//GlobalUnlock(hGlobal);
//CloseHandle(hFile);
//
//LPSTREAM pstm = nullptr;
//CreateStreamOnHGlobal(hGlobal, TRUE, &pstm); // create IStream* from global memory
//
//// Create IPicture from image file
//// if (gpPicture==nullptr)
//// gpPicture->Release();
//HRESULT hr = ::OleLoadPicture(pstm, dwFileSize, FALSE, IID_IPicture, (LPVOID *)&gpPicture);
//ASSERT(SUCCEEDED(hr) && gpPicture);
//pstm->Release();
//}


//culoare de backbround pentru gifuri di png-uri transparente
// HRESULT GetBackgroundColor( IWICMetadataQueryReader * pMetadataQueryReader )
// {
//     DWORD dwBGColor;
//     BYTE backgroundIndex = 0;
//     WICColor rgColors[256];
//     UINT cColorsCopied = 0;
//     PROPVARIANT propVariant;
//     PropVariantInit( &propVariant );
//     IWICPalette * pWicPalette = NULL;
//
//     //If we have a global palette, get the palette and background color
//     hr = pMetadataQueryReader->GetMetadataByName( L"/logscrdesc/GlobalColorTableFlag", &propVariant );
//     hr = ( propVariant.vt != VT_BOOL || !propVariant.boolVal ) ? E_FAIL : S_OK;
//     PropVariantClear( &propVariant );
//
//     if ( SUCCEEDED( hr ) )
//     {
//         hr = pMetadataQueryReader->GetMetadataByName( L"/logscrdesc/BackgroundColorIndex", &propVariant );
//
//         if ( SUCCEEDED( hr ) )
//         {
//             hr = ( propVariant.vt != VT_UI1 ) ? E_FAIL : S_OK;
//
//             if ( SUCCEEDED( hr ) )
//                 backgroundIndex = propVariant.bVal;
//
//             PropVariantClear( &propVariant );
//         }
//     }
//
//     //Get the color from the palette
//     if ( SUCCEEDED( hr ) )
//         hr = m_pIWICFactory->CreatePalette( &pWicPalette );
//
//     if ( SUCCEEDED( hr ) )
//         hr = m_pBitmapDecoder->CopyPalette( pWicPalette ); //Get the global palette
//
//     if ( SUCCEEDED( hr ) )
//         hr = pWicPalette->GetColors( ARRAYSIZE( rgColors ), rgColors, &cColorsCopied );
//
//     if ( SUCCEEDED( hr ) )
//     {
//         hr = ( backgroundIndex >= cColorsCopied ) ? E_FAIL : S_OK;
//     }
//
//     if ( SUCCEEDED( hr ) )
//     {
//         dwBGColor = rgColors[backgroundIndex];
//         FLOAT alpha = ( dwBGColor >> 24 ) / 255.f;
//         m_backgroundColor = D2D1::ColorF( dwBGColor, alpha );
//     }
//
//     SafeRelease( pWicPalette );
//     return hr;
// }
